﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace HtPasswd
{
    class Program
    {
        string[] args;
        List<string> keys = new List<string>();
        bool batchMode;
        bool createPasswdFile;
        bool displayResults;
        string htpasswdPath;
        string username;
        string password;
        
        static void Main(string[] args)
        {
            Program p = new Program();
            p.args = args;
            p.Run();
        }

        void Run()
        {
            // display welcome
            DisplayWelcome();

            // process command line parameters
            if (!ParseArguments())
            {
                DisplayUsage();
                return;
            }

            GeneratePassword();
        }

        void GeneratePassword()
        {
            // request password from command line if required
            if (!batchMode)
            {
                Console.WriteLine("User: " + username);
                Console.Write("Password: ");
                password = ReadPassword();
                Console.WriteLine();
            }

            // encrypt password
            BsdDES des = new BsdDES();
            string cryptPassword = des.Crypt(password);

            if (displayResults)
            {
                // display encrypted password on screen
                Console.WriteLine(username + ":" + cryptPassword);
            }
            else
            {
                // update .passwd file
                if (!createPasswdFile && !File.Exists(htpasswdPath))
                {
                    Console.WriteLine("File not found: " + htpasswdPath);
                    return;
                }

                List<string> updatedLines = new List<string>();
                string[] lines = new string[] { };
                
                // read or delete file
                if (createPasswdFile)
                {
                    try
                    {
                        File.Delete(htpasswdPath);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Could not overwrite original file: " + htpasswdPath);
                        Console.WriteLine(ex.Message);
                    }
                }
                else
                {
                    lines = File.ReadAllLines(htpasswdPath);
                }
                    
                bool found = false;
                foreach (string line in lines)
                {
                    if (line.Trim() != ""
                        && !line.StartsWith("#"))
                    {
                        int colon = line.IndexOf(":");
                        if (colon != -1 && username == line.Substring(0, colon).Trim())
                        {
                            updatedLines.Add(username + ":" + cryptPassword);
                            found = true;
                            continue;
                        }
                    }

                    // preserve original line
                    updatedLines.Add(line);
                }

                // add new record if not found
                if(!found)
                    updatedLines.Add(username + ":" + cryptPassword);

                try
                {
                    File.WriteAllLines(htpasswdPath, updatedLines.ToArray());
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Could not update file: " + htpasswdPath);
                    Console.WriteLine(ex.Message);
                }
            }
        }

        bool ParseArguments()
        {
            if (args.Length == 0)
                return false; // empty list

            for (int i = 0; i < args.Length; i++)
            {
                if (args[i].StartsWith("-"))
                {
                    // parse options
                    string opts = args[i].Substring(1);
                    foreach (char opt in opts)
                    {
                        if (opt == 'c')
                            createPasswdFile = true;
                        else if (opt == 'n')
                            displayResults = true;
                        else if (opt == 'b')
                            batchMode = true;
                    }
                }
                else
                {
                    // non-option
                    if (displayResults && !batchMode)
                    {
                        // username
                        username = args[i];
                        return true;
                    }
                    else if (displayResults && batchMode)
                    {
                        // username & password
                        if ((i + 2) > args.Length)
                            return false;
                        username = args[i];
                        password = args[i + 1];
                        return true;
                    }
                    else if (!displayResults && batchMode)
                    {
                        // htpasswdfile & username & password
                        if ((i + 3) > args.Length)
                            return false;
                        htpasswdPath = args[i];
                        username = args[i + 1];
                        password = args[i + 2];
                        return true;
                    }
                    else if (!displayResults && !batchMode)
                    {
                        // htpasswdfile & username
                        if ((i + 2) > args.Length)
                            return false;
                        htpasswdPath = args[i];
                        username = args[i + 1];
                        return true;
                    }
                }
            }

            return true;
        }

        string ReadPassword()
        {
            string password = "";
            do
            {
                ConsoleKeyInfo key = Console.ReadKey(true);
                if (key.Key == ConsoleKey.Enter)
                    break;

                Console.Write('*');
                password += key.KeyChar;
            } while (true);
            return password;
        }

        void DisplayWelcome()
        {
            Console.WriteLine("htpasswd - Create and update user authentication files");
        }

        void DisplayUsage()
        {
            Console.WriteLine("");
            Console.WriteLine("USAGE");
            Console.WriteLine("     htpasswd [ -c ] passwdfile username");
            Console.WriteLine("     htpasswd -b [ -c ] passwdfile username password");
            Console.WriteLine("     htpasswd -n username");
            Console.WriteLine("     htpasswd -nb username password");
            Console.WriteLine("");
            Console.WriteLine("OPTIONS");
            Console.WriteLine("-c   Create the passwdfile. If passwdfile already exists, it");
            Console.WriteLine("     is rewritten and truncated.  This option cannot be com-");
            Console.WriteLine("     bined with the -n option.");
            Console.WriteLine("");
            Console.WriteLine("-n   Display the results  on  standard  output  rather  than");
            Console.WriteLine("     updating  a  file.  This option changes the syntax of ");
            Console.WriteLine("     the command  line,  since  the  passwdfile  argument");
            Console.WriteLine("     (usually  the first one) is omitted.  It cannot be com-");
            Console.WriteLine("     bined with the -c option.");
            Console.WriteLine("");
            Console.WriteLine("-b   Use batch mode; i.e., get the password from the command");
            Console.WriteLine("     line  rather  than prompting for it. This option should");
            Console.WriteLine("     be used  with  extreme  care,  since  the  password  is");
            Console.WriteLine("     clearly visible on the command line.");



        }
    }
}
